{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# 代码说明：\n",
    "> 代码共分为三大部分，所有部分均使用epoch_num=10 \n",
    "## 第一部分\n",
    "有原来的学习率衰减函数+新的图表展示构成。\n",
    "\n",
    "## 第二部分\n",
    "是完成的作业，使用API中的衰减函数+loss图表构成\n",
    "\n",
    "## 第三部分\n",
    "是完成的作业，使用的是测试集的数据+loss图表构成"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "本代码旨在于使用ResNet进行眼睑筛查，代码已经完成，可以直接运行。\n",
    "\n",
    "**题目要求**：\n",
    "1. 通过查阅API，使用衰减学习率，通过多次调参数，找到一个最佳的衰减步长，使得loss比原代码中下降的更快\n",
    "2. 请自行绘制修改学习率前后的loss衰减图\n",
    "\n",
    "**注意**：\n",
    "1. 原代码中仅需要更改学习率部分\n",
    "2. 若loss下降效果不明显，可自行调大epoch_num至10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# 初次运行时将注释取消，以便解压文件\r\n",
    "# 如果已经解压过了，则不需要运行此段代码，否则文件已经存在解压会报错\r\n",
    "# !unzip -o -q -d /home/aistudio/work/palm /home/aistudio/data/data23828//training.zip\r\n",
    "# %cd /home/aistudio/work/palm/PALM-Training400/\r\n",
    "# !unzip -o -q PALM-Training400.zip\r\n",
    "# !unzip -o -q -d /home/aistudio/work/palm /home/aistudio/data/data23828//validation.zip\r\n",
    "# !unzip -o -q -d /home/aistudio/work/palm /home/aistudio/data/data23828//valid_gt.zip"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2020-08-22 03:48:39,462-INFO: font search path ['/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/mpl-data/fonts/ttf', '/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/mpl-data/fonts/afm', '/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts']\n",
      "2020-08-22 03:48:39,956-INFO: generated new fontManager\n"
     ]
    }
   ],
   "source": [
    "import cv2\r\n",
    "import random\r\n",
    "import numpy as np\r\n",
    "import matplotlib.pyplot as plt\r\n",
    "# 对读入的图像数据进行预处理\r\n",
    "def transform_img(img):\r\n",
    "    # 将图片尺寸缩放道 224x224\r\n",
    "    img = cv2.resize(img, (224, 224))\r\n",
    "    # 读入的图像数据格式是[H, W, C]\r\n",
    "    # 使用转置操作将其变成[C, H, W]\r\n",
    "    img = np.transpose(img, (2,0,1))\r\n",
    "    img = img.astype('float32')\r\n",
    "    # 将数据范围调整到[-1.0, 1.0]之间\r\n",
    "    img = img / 255.\r\n",
    "    img = img * 2.0 - 1.0\r\n",
    "    return img\r\n",
    "\r\n",
    "# 定义训练集数据读取器\r\n",
    "def data_loader(datadir, batch_size=10, mode = 'train'):\r\n",
    "    # 将datadir目录下的文件列出来，每条文件都要读入\r\n",
    "    filenames = os.listdir(datadir)\r\n",
    "    def reader():\r\n",
    "        if mode == 'train':\r\n",
    "            # 训练时随机打乱数据顺序\r\n",
    "            random.shuffle(filenames)\r\n",
    "        batch_imgs = []\r\n",
    "        batch_labels = []\r\n",
    "        for name in filenames:\r\n",
    "            filepath = os.path.join(datadir, name)\r\n",
    "            img = cv2.imread(filepath)\r\n",
    "            img = transform_img(img)\r\n",
    "            if name[0] == 'H' or name[0] == 'N':\r\n",
    "                # H开头的文件名表示高度近似，N开头的文件名表示正常视力\r\n",
    "                # 高度近视和正常视力的样本，都不是病理性的，属于负样本，标签为0\r\n",
    "                label = 0\r\n",
    "            elif name[0] == 'P':\r\n",
    "                # P开头的是病理性近视，属于正样本，标签为1\r\n",
    "                label = 1\r\n",
    "            else:\r\n",
    "                raise('Not excepted file name')\r\n",
    "            # 每读取一个样本的数据，就将其放入数据列表中\r\n",
    "            batch_imgs.append(img)\r\n",
    "            batch_labels.append(label)\r\n",
    "            if len(batch_imgs) == batch_size:\r\n",
    "                # 当数据列表的长度等于batch_size的时候，\r\n",
    "                # 把这些数据当作一个mini-batch，并作为数据生成器的一个输出\r\n",
    "                imgs_array = np.array(batch_imgs).astype('float32')\r\n",
    "                labels_array = np.array(batch_labels).astype('float32').reshape(-1, 1)\r\n",
    "                yield imgs_array, labels_array\r\n",
    "                batch_imgs = []\r\n",
    "                batch_labels = []\r\n",
    "\r\n",
    "        if len(batch_imgs) > 0:\r\n",
    "            # 剩余样本数目不足一个batch_size的数据，一起打包成一个mini-batch\r\n",
    "            imgs_array = np.array(batch_imgs).astype('float32')\r\n",
    "            labels_array = np.array(batch_labels).astype('float32').reshape(-1, 1)\r\n",
    "            yield imgs_array, labels_array\r\n",
    "\r\n",
    "    return reader\r\n",
    "\r\n",
    "# 定义验证集数据读取器\r\n",
    "def valid_data_loader(datadir, csvfile, batch_size=10, mode='valid'):\r\n",
    "    # 训练集读取时通过文件名来确定样本标签，验证集则通过csvfile来读取每个图片对应的标签\r\n",
    "    # 请查看解压后的验证集标签数据，观察csvfile文件里面所包含的内容\r\n",
    "    # csvfile文件所包含的内容格式如下，每一行代表一个样本，\r\n",
    "    # 其中第一列是图片id，第二列是文件名，第三列是图片标签，\r\n",
    "    # 第四列和第五列是Fovea的坐标，与分类任务无关\r\n",
    "    # ID,imgName,Label,Fovea_X,Fovea_Y\r\n",
    "    # 1,V0001.jpg,0,1157.74,1019.87\r\n",
    "    # 2,V0002.jpg,1,1285.82,1080.47\r\n",
    "    # 打开包含验证集标签的csvfile，并读入其中的内容\r\n",
    "    filelists = open(csvfile).readlines()\r\n",
    "    def reader():\r\n",
    "        batch_imgs = []\r\n",
    "        batch_labels = []\r\n",
    "        for line in filelists[1:]:\r\n",
    "            line = line.strip().split(',')\r\n",
    "            name = line[1]\r\n",
    "            label = int(line[2])\r\n",
    "            # 根据图片文件名加载图片，并对图像数据作预处理\r\n",
    "            filepath = os.path.join(datadir, name)\r\n",
    "            img = cv2.imread(filepath)\r\n",
    "            img = transform_img(img)\r\n",
    "            # 每读取一个样本的数据，就将其放入数据列表中\r\n",
    "            batch_imgs.append(img)\r\n",
    "            batch_labels.append(label)\r\n",
    "            if len(batch_imgs) == batch_size:\r\n",
    "                # 当数据列表的长度等于batch_size的时候，\r\n",
    "                # 把这些数据当作一个mini-batch，并作为数据生成器的一个输出\r\n",
    "                imgs_array = np.array(batch_imgs).astype('float32')\r\n",
    "                labels_array = np.array(batch_labels).astype('float32').reshape(-1, 1)\r\n",
    "                yield imgs_array, labels_array\r\n",
    "                batch_imgs = []\r\n",
    "                batch_labels = []\r\n",
    "\r\n",
    "        if len(batch_imgs) > 0:\r\n",
    "            # 剩余样本数目不足一个batch_size的数据，一起打包成一个mini-batch\r\n",
    "            imgs_array = np.array(batch_imgs).astype('float32')\r\n",
    "            labels_array = np.array(batch_labels).astype('float32').reshape(-1, 1)\r\n",
    "            yield imgs_array, labels_array\r\n",
    "\r\n",
    "    return reader"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# -*- coding: utf-8 -*-\r\n",
    "\r\n",
    "# LeNet 识别眼疾图片\r\n",
    "\r\n",
    "import os\r\n",
    "import random\r\n",
    "import paddle\r\n",
    "import paddle.fluid as fluid\r\n",
    "import numpy as np\r\n",
    "\r\n",
    "DATADIR = '/home/aistudio/work/palm/PALM-Training400/PALM-Training400'\r\n",
    "DATADIR2 = '/home/aistudio/work/palm/PALM-Validation400'\r\n",
    "CSVFILE = '/home/aistudio/labels.csv'\r\n",
    "iter=0\r\n",
    "iters=[]\r\n",
    "losses1 = []\r\n",
    "\r\n",
    "\r\n",
    "# 定义训练过程\r\n",
    "def train(model):\r\n",
    "    with fluid.dygraph.guard():\r\n",
    "        print('开始训练 ... ')\r\n",
    "        model.train()\r\n",
    "        epoch_num = 10\r\n",
    "        # 定义优化器\r\n",
    "        opt = fluid.optimizer.Momentum(learning_rate=0.001, momentum=0.9, parameter_list=model.parameters())\r\n",
    "        # 定义数据读取器，训练数据读取器和验证数据读取器\r\n",
    "        train_loader = data_loader(DATADIR, batch_size=10, mode='train')\r\n",
    "        valid_loader = valid_data_loader(DATADIR2, CSVFILE)\r\n",
    "        for epoch in range(epoch_num):\r\n",
    "            for batch_id, data in enumerate(train_loader()):\r\n",
    "                x_data, y_data = data\r\n",
    "                img = fluid.dygraph.to_variable(x_data)\r\n",
    "                label = fluid.dygraph.to_variable(y_data)\r\n",
    "                # 运行模型前向计算，得到预测值\r\n",
    "                logits = model(img)\r\n",
    "                # 进行loss计算\r\n",
    "                loss = fluid.layers.sigmoid_cross_entropy_with_logits(logits, label)\r\n",
    "                avg_loss = fluid.layers.mean(loss)\r\n",
    "\r\n",
    "                if batch_id % 10 == 0:\r\n",
    "                    print(\"epoch: {}, batch_id: {}, loss is: {}\".format(epoch, batch_id, avg_loss.numpy()))\r\n",
    "                global iter\r\n",
    "                global iters\r\n",
    "                global losses1\r\n",
    "                iters.append(iter)\r\n",
    "                losses1.append(avg_loss.numpy())\r\n",
    "                iter = iter + 10\r\n",
    "\r\n",
    "                # 反向传播，更新权重，清除梯度\r\n",
    "                avg_loss.backward()\r\n",
    "                opt.minimize(avg_loss)\r\n",
    "                model.clear_gradients()\r\n",
    "\r\n",
    "            model.eval()\r\n",
    "            accuracies = []\r\n",
    "            losses = []\r\n",
    "            for batch_id, data in enumerate(valid_loader()):\r\n",
    "                x_data, y_data = data\r\n",
    "                img = fluid.dygraph.to_variable(x_data)\r\n",
    "                label = fluid.dygraph.to_variable(y_data)\r\n",
    "                # 运行模型前向计算，得到预测值\r\n",
    "                logits = model(img)\r\n",
    "                # 二分类，sigmoid计算后的结果以0.5为阈值分两个类别\r\n",
    "                # 计算sigmoid后的预测概率，进行loss计算\r\n",
    "                pred = fluid.layers.sigmoid(logits)\r\n",
    "                loss = fluid.layers.sigmoid_cross_entropy_with_logits(logits, label)\r\n",
    "                # 计算预测概率小于0.5的类别\r\n",
    "                pred2 = pred * (-1.0) + 1.0\r\n",
    "                # 得到两个类别的预测概率，并沿第一个维度级联\r\n",
    "                pred = fluid.layers.concat([pred2, pred], axis=1)\r\n",
    "                acc = fluid.layers.accuracy(pred, fluid.layers.cast(label, dtype='int64'))\r\n",
    "                accuracies.append(acc.numpy())\r\n",
    "                losses.append(loss.numpy())\r\n",
    "            print(\"[validation] accuracy/loss: {}/{}\".format(np.mean(accuracies), np.mean(losses)))\r\n",
    "            model.train()\r\n",
    "\r\n",
    "        # save params of model\r\n",
    "        fluid.save_dygraph(model.state_dict(), 'palm')\r\n",
    "        # save optimizer state\r\n",
    "        fluid.save_dygraph(opt.state_dict(), 'palm')\r\n",
    "\r\n",
    "\r\n",
    "# 定义评估过程\r\n",
    "def evaluation(model, params_file_path):\r\n",
    "    \r\n",
    "    with fluid.dygraph.guard():\r\n",
    "        print('开始评估 .......')\r\n",
    "        #加载模型参数\r\n",
    "        model_state_dict, _ = fluid.load_dygraph(params_file_path)\r\n",
    "        model.load_dict(model_state_dict)\r\n",
    "\r\n",
    "        model.eval()\r\n",
    "        eval_loader = data_loader(DATADIR, \r\n",
    "                           batch_size=10, mode='eval')\r\n",
    "\r\n",
    "        acc_set = []\r\n",
    "        avg_loss_set = []\r\n",
    "        for batch_id, data in enumerate(eval_loader()):\r\n",
    "            x_data, y_data = data\r\n",
    "            img = fluid.dygraph.to_variable(x_data)\r\n",
    "            label = fluid.dygraph.to_variable(y_data)\r\n",
    "            y_data = y_data.astype(np.int64)\r\n",
    "            label_64 = fluid.dygraph.to_variable(y_data)\r\n",
    "            # 计算预测和精度\r\n",
    "            prediction, acc = model(img, label_64)\r\n",
    "            # 计算损失函数值\r\n",
    "            loss = fluid.layers.sigmoid_cross_entropy_with_logits(prediction, label)\r\n",
    "            avg_loss = fluid.layers.mean(loss)\r\n",
    "            acc_set.append(float(acc.numpy()))\r\n",
    "            avg_loss_set.append(float(avg_loss.numpy()))\r\n",
    "        # 求平均精度\r\n",
    "        acc_val_mean = np.array(acc_set).mean()\r\n",
    "        avg_loss_val_mean = np.array(avg_loss_set).mean()\r\n",
    "\r\n",
    "        print('loss={}, acc={}'.format(avg_loss_val_mean, acc_val_mean))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# -*- coding:utf-8 -*-\r\n",
    "\r\n",
    "# ResNet模型代码\r\n",
    "import numpy as np\r\n",
    "import paddle\r\n",
    "import paddle.fluid as fluid\r\n",
    "from paddle.fluid.layer_helper import LayerHelper\r\n",
    "from paddle.fluid.dygraph.nn import Conv2D, Pool2D, BatchNorm, Linear\r\n",
    "from paddle.fluid.dygraph.base import to_variable\r\n",
    "\r\n",
    "# ResNet中使用了BatchNorm层，在卷积层的后面加上BatchNorm以提升数值稳定性\r\n",
    "# 定义卷积批归一化块\r\n",
    "class ConvBNLayer(fluid.dygraph.Layer):\r\n",
    "    def __init__(self,\r\n",
    "                 num_channels,\r\n",
    "                 num_filters,\r\n",
    "                 filter_size,\r\n",
    "                 stride=1,\r\n",
    "                 groups=1,\r\n",
    "                 act=None):\r\n",
    "        \"\"\"\r\n",
    "        \r\n",
    "        num_channels, 卷积层的输入通道数\r\n",
    "        num_filters, 卷积层的输出通道数\r\n",
    "        stride, 卷积层的步幅\r\n",
    "        groups, 分组卷积的组数，默认groups=1不使用分组卷积\r\n",
    "        act, 激活函数类型，默认act=None不使用激活函数\r\n",
    "        \"\"\"\r\n",
    "        super(ConvBNLayer, self).__init__()\r\n",
    "\r\n",
    "        # 创建卷积层\r\n",
    "        self._conv = Conv2D(\r\n",
    "            num_channels=num_channels,\r\n",
    "            num_filters=num_filters,\r\n",
    "            filter_size=filter_size,\r\n",
    "            stride=stride,\r\n",
    "            padding=(filter_size - 1) // 2,\r\n",
    "            groups=groups,\r\n",
    "            act=None,\r\n",
    "            bias_attr=False)\r\n",
    "\r\n",
    "        # 创建BatchNorm层\r\n",
    "        self._batch_norm = BatchNorm(num_filters, act=act)\r\n",
    "\r\n",
    "    def forward(self, inputs):\r\n",
    "        y = self._conv(inputs)\r\n",
    "        y = self._batch_norm(y)\r\n",
    "        return y\r\n",
    "\r\n",
    "# 定义残差块\r\n",
    "# 每个残差块会对输入图片做三次卷积，然后跟输入图片进行短接\r\n",
    "# 如果残差块中第三次卷积输出特征图的形状与输入不一致，则对输入图片做1x1卷积，将其输出形状调整成一致\r\n",
    "class BottleneckBlock(fluid.dygraph.Layer):\r\n",
    "    def __init__(self,\r\n",
    "                 num_channels,\r\n",
    "                 num_filters,\r\n",
    "                 stride,\r\n",
    "                 shortcut=True):\r\n",
    "        super(BottleneckBlock, self).__init__()\r\n",
    "        # 创建第一个卷积层 1x1\r\n",
    "        self.conv0 = ConvBNLayer(\r\n",
    "            num_channels=num_channels,\r\n",
    "            num_filters=num_filters,\r\n",
    "            filter_size=1,\r\n",
    "            act='relu')\r\n",
    "        # 创建第二个卷积层 3x3\r\n",
    "        self.conv1 = ConvBNLayer(\r\n",
    "            num_channels=num_filters,\r\n",
    "            num_filters=num_filters,\r\n",
    "            filter_size=3,\r\n",
    "            stride=stride,\r\n",
    "            act='relu')\r\n",
    "        # 创建第三个卷积 1x1，但输出通道数乘以4\r\n",
    "        self.conv2 = ConvBNLayer(\r\n",
    "            num_channels=num_filters,\r\n",
    "            num_filters=num_filters * 4,\r\n",
    "            filter_size=1,\r\n",
    "            act=None)\r\n",
    "\r\n",
    "        # 如果conv2的输出跟此残差块的输入数据形状一致，则shortcut=True\r\n",
    "        # 否则shortcut = False，添加1个1x1的卷积作用在输入数据上，使其形状变成跟conv2一致\r\n",
    "        if not shortcut:\r\n",
    "            self.short = ConvBNLayer(\r\n",
    "                num_channels=num_channels,\r\n",
    "                num_filters=num_filters * 4,\r\n",
    "                filter_size=1,\r\n",
    "                stride=stride)\r\n",
    "\r\n",
    "        self.shortcut = shortcut\r\n",
    "\r\n",
    "        self._num_channels_out = num_filters * 4\r\n",
    "\r\n",
    "    def forward(self, inputs):\r\n",
    "        y = self.conv0(inputs)\r\n",
    "        conv1 = self.conv1(y)\r\n",
    "        conv2 = self.conv2(conv1)\r\n",
    "\r\n",
    "        # 如果shortcut=True，直接将inputs跟conv2的输出相加\r\n",
    "        # 否则需要对inputs进行一次卷积，将形状调整成跟conv2输出一致\r\n",
    "        if self.shortcut:\r\n",
    "            short = inputs\r\n",
    "        else:\r\n",
    "            short = self.short(inputs)\r\n",
    "\r\n",
    "        y = fluid.layers.elementwise_add(x=short, y=conv2)\r\n",
    "        layer_helper = LayerHelper(self.full_name(), act='relu')\r\n",
    "        return layer_helper.append_activation(y)\r\n",
    "\r\n",
    "# 定义ResNet模型\r\n",
    "class ResNet(fluid.dygraph.Layer):\r\n",
    "    def __init__(self, layers=50, class_dim=1):\r\n",
    "        \"\"\"\r\n",
    "        \r\n",
    "        layers, 网络层数，可以是50, 101或者152\r\n",
    "        class_dim，分类标签的类别数\r\n",
    "        \"\"\"\r\n",
    "        super(ResNet, self).__init__()\r\n",
    "        self.layers = layers\r\n",
    "        supported_layers = [50, 101, 152]\r\n",
    "        assert layers in supported_layers, \\\r\n",
    "            \"supported layers are {} but input layer is {}\".format(supported_layers, layers)\r\n",
    "\r\n",
    "        if layers == 50:\r\n",
    "            #ResNet50包含多个模块，其中第2到第5个模块分别包含3、4、6、3个残差块\r\n",
    "            depth = [3, 4, 6, 3]\r\n",
    "        elif layers == 101:\r\n",
    "            #ResNet101包含多个模块，其中第2到第5个模块分别包含3、4、23、3个残差块\r\n",
    "            depth = [3, 4, 23, 3]\r\n",
    "        elif layers == 152:\r\n",
    "            #ResNet50包含多个模块，其中第2到第5个模块分别包含3、8、36、3个残差块\r\n",
    "            depth = [3, 8, 36, 3]\r\n",
    "        \r\n",
    "        # 残差块中使用到的卷积的输出通道数\r\n",
    "        num_filters = [64, 128, 256, 512]\r\n",
    "\r\n",
    "        # ResNet的第一个模块，包含1个7x7卷积，后面跟着1个最大池化层\r\n",
    "        self.conv = ConvBNLayer(\r\n",
    "            num_channels=3,\r\n",
    "            num_filters=64,\r\n",
    "            filter_size=7,\r\n",
    "            stride=2,\r\n",
    "            act='relu')\r\n",
    "        self.pool2d_max = Pool2D(\r\n",
    "            pool_size=3,\r\n",
    "            pool_stride=2,\r\n",
    "            pool_padding=1,\r\n",
    "            pool_type='max')\r\n",
    "\r\n",
    "        # ResNet的第二到第五个模块c2、c3、c4、c5\r\n",
    "        self.bottleneck_block_list = []\r\n",
    "        num_channels = 64\r\n",
    "        for block in range(len(depth)):\r\n",
    "            shortcut = False\r\n",
    "            for i in range(depth[block]):\r\n",
    "                bottleneck_block = self.add_sublayer(\r\n",
    "                    'bb_%d_%d' % (block, i),\r\n",
    "                    BottleneckBlock(\r\n",
    "                        num_channels=num_channels,\r\n",
    "                        num_filters=num_filters[block],\r\n",
    "                        stride=2 if i == 0 and block != 0 else 1, # c3、c4、c5将会在第一个残差块使用stride=2；其余所有残差块stride=1\r\n",
    "                        shortcut=shortcut))\r\n",
    "                num_channels = bottleneck_block._num_channels_out\r\n",
    "                self.bottleneck_block_list.append(bottleneck_block)\r\n",
    "                shortcut = True\r\n",
    "\r\n",
    "        # 在c5的输出特征图上使用全局池化\r\n",
    "        self.pool2d_avg = Pool2D(pool_size=7, pool_type='avg', global_pooling=True)\r\n",
    "\r\n",
    "        # stdv用来作为全连接层随机初始化参数的方差\r\n",
    "        import math\r\n",
    "        stdv = 1.0 / math.sqrt(2048 * 1.0)\r\n",
    "        \r\n",
    "        # 创建全连接层，输出大小为类别数目\r\n",
    "        self.out = Linear(input_dim=2048, output_dim=class_dim,\r\n",
    "                      param_attr=fluid.param_attr.ParamAttr(\r\n",
    "                          initializer=fluid.initializer.Uniform(-stdv, stdv)))\r\n",
    "\r\n",
    "        \r\n",
    "    def forward(self, inputs):\r\n",
    "        y = self.conv(inputs)\r\n",
    "        y = self.pool2d_max(y)\r\n",
    "        for bottleneck_block in self.bottleneck_block_list:\r\n",
    "            y = bottleneck_block(y)\r\n",
    "        y = self.pool2d_avg(y)\r\n",
    "        y = fluid.layers.reshape(y, [y.shape[0], -1])\r\n",
    "        y = self.out(y)\r\n",
    "        return y\r\n",
    "\r\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "start training ... \n",
      "epoch: 0, batch_id: 0, loss is: [0.7799246]\n",
      "epoch: 0, batch_id: 10, loss is: [1.0568984]\n",
      "epoch: 0, batch_id: 20, loss is: [0.65656614]\n",
      "epoch: 0, batch_id: 30, loss is: [0.95643485]\n",
      "[validation] accuracy/loss: 0.7324999570846558/0.6834019422531128\n",
      "epoch: 1, batch_id: 0, loss is: [0.77571315]\n",
      "epoch: 1, batch_id: 10, loss is: [0.35313588]\n",
      "epoch: 1, batch_id: 20, loss is: [0.602845]\n",
      "epoch: 1, batch_id: 30, loss is: [0.80204993]\n",
      "[validation] accuracy/loss: 0.6550000309944153/0.6528813242912292\n",
      "epoch: 2, batch_id: 0, loss is: [0.64695567]\n",
      "epoch: 2, batch_id: 10, loss is: [0.3700227]\n",
      "epoch: 2, batch_id: 20, loss is: [0.81157005]\n",
      "epoch: 2, batch_id: 30, loss is: [1.4227395]\n",
      "[validation] accuracy/loss: 0.9049999117851257/0.2212667465209961\n",
      "epoch: 3, batch_id: 0, loss is: [0.21705833]\n",
      "epoch: 3, batch_id: 10, loss is: [0.09245652]\n",
      "epoch: 3, batch_id: 20, loss is: [0.11358038]\n",
      "epoch: 3, batch_id: 30, loss is: [0.03537735]\n",
      "[validation] accuracy/loss: 0.862500011920929/0.2991602420806885\n",
      "epoch: 4, batch_id: 0, loss is: [0.4533707]\n",
      "epoch: 4, batch_id: 10, loss is: [0.9464125]\n",
      "epoch: 4, batch_id: 20, loss is: [0.33605057]\n",
      "epoch: 4, batch_id: 30, loss is: [0.34232295]\n",
      "[validation] accuracy/loss: 0.9475000500679016/0.16630718111991882\n",
      "epoch: 5, batch_id: 0, loss is: [0.13738397]\n",
      "epoch: 5, batch_id: 10, loss is: [0.2865173]\n",
      "epoch: 5, batch_id: 20, loss is: [0.10168681]\n",
      "epoch: 5, batch_id: 30, loss is: [0.01889497]\n",
      "[validation] accuracy/loss: 0.9524999856948853/0.13858918845653534\n",
      "epoch: 6, batch_id: 0, loss is: [0.07006306]\n",
      "epoch: 6, batch_id: 10, loss is: [0.1144826]\n",
      "epoch: 6, batch_id: 20, loss is: [0.30475038]\n",
      "epoch: 6, batch_id: 30, loss is: [0.19729209]\n",
      "[validation] accuracy/loss: 0.9350000619888306/0.17197586596012115\n",
      "epoch: 7, batch_id: 0, loss is: [0.08109235]\n",
      "epoch: 7, batch_id: 10, loss is: [0.5064727]\n",
      "epoch: 7, batch_id: 20, loss is: [0.17536843]\n",
      "epoch: 7, batch_id: 30, loss is: [0.07893965]\n",
      "[validation] accuracy/loss: 0.95250004529953/0.15534786880016327\n",
      "epoch: 8, batch_id: 0, loss is: [0.0133496]\n",
      "epoch: 8, batch_id: 10, loss is: [0.07158424]\n",
      "epoch: 8, batch_id: 20, loss is: [0.5031681]\n",
      "epoch: 8, batch_id: 30, loss is: [0.38113096]\n",
      "[validation] accuracy/loss: 0.9475000500679016/0.17671695351600647\n",
      "epoch: 9, batch_id: 0, loss is: [0.07077305]\n",
      "epoch: 9, batch_id: 10, loss is: [0.7638706]\n",
      "epoch: 9, batch_id: 20, loss is: [0.01601633]\n",
      "epoch: 9, batch_id: 30, loss is: [0.0679725]\n",
      "[validation] accuracy/loss: 0.9574999809265137/0.1406005620956421\n"
     ]
    }
   ],
   "source": [
    "with fluid.dygraph.guard():\r\n",
    "    model = ResNet()\r\n",
    "\r\n",
    "train(model)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#画出训练过程中Loss的变化曲线\n",
    "\n",
    "plt.figure()\n",
    "plt.title(\"train loss\", fontsize=24)\n",
    "plt.xlabel(\"iter\", fontsize=14)\n",
    "plt.ylabel(\"loss\", fontsize=14)\n",
    "plt.plot(iters, losses1,color='red',label='train loss') \n",
    "plt.grid()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "## 第二部分\n",
    "> 是完成的作业，使用API中的衰减函数+loss图表构成"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "start training ... \n",
      "epoch: 0, batch_id: 0, loss is: [0.73158044]\n",
      "epoch: 0, batch_id: 10, loss is: [0.5884239]\n",
      "epoch: 0, batch_id: 20, loss is: [0.40848133]\n",
      "epoch: 0, batch_id: 30, loss is: [0.4146288]\n",
      "[validation] accuracy/loss: 0.9100000262260437/0.25766390562057495\n",
      "epoch: 1, batch_id: 0, loss is: [0.12713009]\n",
      "epoch: 1, batch_id: 10, loss is: [0.32762435]\n",
      "epoch: 1, batch_id: 20, loss is: [0.40109816]\n",
      "epoch: 1, batch_id: 30, loss is: [0.1286005]\n",
      "[validation] accuracy/loss: 0.9600000381469727/0.1530722975730896\n",
      "epoch: 2, batch_id: 0, loss is: [0.07444803]\n",
      "epoch: 2, batch_id: 10, loss is: [0.19276017]\n",
      "epoch: 2, batch_id: 20, loss is: [0.24117084]\n",
      "epoch: 2, batch_id: 30, loss is: [0.05187465]\n",
      "[validation] accuracy/loss: 0.9624999761581421/0.13815847039222717\n",
      "epoch: 3, batch_id: 0, loss is: [0.07692231]\n",
      "epoch: 3, batch_id: 10, loss is: [0.09804794]\n",
      "epoch: 3, batch_id: 20, loss is: [0.08138321]\n",
      "epoch: 3, batch_id: 30, loss is: [0.03015644]\n",
      "[validation] accuracy/loss: 0.9424999356269836/0.16447362303733826\n",
      "epoch: 4, batch_id: 0, loss is: [0.254558]\n",
      "epoch: 4, batch_id: 10, loss is: [0.03515944]\n",
      "epoch: 4, batch_id: 20, loss is: [0.06182344]\n",
      "epoch: 4, batch_id: 30, loss is: [0.03856962]\n",
      "[validation] accuracy/loss: 0.9649999737739563/0.12459144741296768\n",
      "epoch: 5, batch_id: 0, loss is: [0.13468699]\n",
      "epoch: 5, batch_id: 10, loss is: [0.41926718]\n",
      "epoch: 5, batch_id: 20, loss is: [0.21022578]\n",
      "epoch: 5, batch_id: 30, loss is: [0.22295657]\n",
      "[validation] accuracy/loss: 0.9649999737739563/0.11531486362218857\n",
      "epoch: 6, batch_id: 0, loss is: [0.35883653]\n",
      "epoch: 6, batch_id: 10, loss is: [0.04974422]\n",
      "epoch: 6, batch_id: 20, loss is: [0.09543474]\n",
      "epoch: 6, batch_id: 30, loss is: [0.19396298]\n",
      "[validation] accuracy/loss: 0.9624999761581421/0.12470817565917969\n",
      "epoch: 7, batch_id: 0, loss is: [0.03046144]\n",
      "epoch: 7, batch_id: 10, loss is: [0.57724357]\n",
      "epoch: 7, batch_id: 20, loss is: [0.18632355]\n",
      "epoch: 7, batch_id: 30, loss is: [0.06215224]\n",
      "[validation] accuracy/loss: 0.9700000882148743/0.10429012030363083\n",
      "epoch: 8, batch_id: 0, loss is: [0.3840809]\n",
      "epoch: 8, batch_id: 10, loss is: [0.21950646]\n",
      "epoch: 8, batch_id: 20, loss is: [0.07367916]\n",
      "epoch: 8, batch_id: 30, loss is: [0.1183652]\n",
      "[validation] accuracy/loss: 0.9700000882148743/0.1060996949672699\n",
      "epoch: 9, batch_id: 0, loss is: [0.19854856]\n",
      "epoch: 9, batch_id: 10, loss is: [0.02847435]\n",
      "epoch: 9, batch_id: 20, loss is: [0.24765183]\n",
      "epoch: 9, batch_id: 30, loss is: [0.04120634]\n",
      "[validation] accuracy/loss: 0.9675000309944153/0.10559286177158356\n"
     ]
    }
   ],
   "source": [
    "import cv2\n",
    "import random\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# 对读入的图像数据进行预处理\n",
    "def transform_img(img):\n",
    "    # 将图片尺寸缩放道 224x224\n",
    "    img = cv2.resize(img, (224, 224))\n",
    "    # 读入的图像数据格式是[H, W, C]\n",
    "    # 使用转置操作将其变成[C, H, W]\n",
    "    img = np.transpose(img, (2,0,1))\n",
    "    img = img.astype('float32')\n",
    "    # 将数据范围调整到[-1.0, 1.0]之间\n",
    "    img = img / 255.\n",
    "    img = img * 2.0 - 1.0\n",
    "    return img\n",
    "\n",
    "# 定义训练集数据读取器\n",
    "def data_loader(datadir, batch_size=10, mode = 'train'):\n",
    "    # 将datadir目录下的文件列出来，每条文件都要读入\n",
    "    filenames = os.listdir(datadir)\n",
    "    def reader():\n",
    "        if mode == 'train':\n",
    "            # 训练时随机打乱数据顺序\n",
    "            random.shuffle(filenames)\n",
    "        batch_imgs = []\n",
    "        batch_labels = []\n",
    "        for name in filenames:\n",
    "            filepath = os.path.join(datadir, name)\n",
    "            img = cv2.imread(filepath)\n",
    "            img = transform_img(img)\n",
    "            if name[0] == 'H' or name[0] == 'N':\n",
    "                # H开头的文件名表示高度近似，N开头的文件名表示正常视力\n",
    "                # 高度近视和正常视力的样本，都不是病理性的，属于负样本，标签为0\n",
    "                label = 0\n",
    "            elif name[0] == 'P':\n",
    "                # P开头的是病理性近视，属于正样本，标签为1\n",
    "                label = 1\n",
    "            else:\n",
    "                raise('Not excepted file name')\n",
    "            # 每读取一个样本的数据，就将其放入数据列表中\n",
    "            batch_imgs.append(img)\n",
    "            batch_labels.append(label)\n",
    "            if len(batch_imgs) == batch_size:\n",
    "                # 当数据列表的长度等于batch_size的时候，\n",
    "                # 把这些数据当作一个mini-batch，并作为数据生成器的一个输出\n",
    "                imgs_array = np.array(batch_imgs).astype('float32')\n",
    "                labels_array = np.array(batch_labels).astype('float32').reshape(-1, 1)\n",
    "                yield imgs_array, labels_array\n",
    "                batch_imgs = []\n",
    "                batch_labels = []\n",
    "\n",
    "        if len(batch_imgs) > 0:\n",
    "            # 剩余样本数目不足一个batch_size的数据，一起打包成一个mini-batch\n",
    "            imgs_array = np.array(batch_imgs).astype('float32')\n",
    "            labels_array = np.array(batch_labels).astype('float32').reshape(-1, 1)\n",
    "            yield imgs_array, labels_array\n",
    "\n",
    "    return reader\n",
    "\n",
    "# 定义验证集数据读取器\n",
    "def valid_data_loader(datadir, csvfile, batch_size=10, mode='valid'):\n",
    "    # 训练集读取时通过文件名来确定样本标签，验证集则通过csvfile来读取每个图片对应的标签\n",
    "    # 请查看解压后的验证集标签数据，观察csvfile文件里面所包含的内容\n",
    "    # csvfile文件所包含的内容格式如下，每一行代表一个样本，\n",
    "    # 其中第一列是图片id，第二列是文件名，第三列是图片标签，\n",
    "    # 第四列和第五列是Fovea的坐标，与分类任务无关\n",
    "    # ID,imgName,Label,Fovea_X,Fovea_Y\n",
    "    # 1,V0001.jpg,0,1157.74,1019.87\n",
    "    # 2,V0002.jpg,1,1285.82,1080.47\n",
    "    # 打开包含验证集标签的csvfile，并读入其中的内容\n",
    "    filelists = open(csvfile).readlines()\n",
    "    def reader():\n",
    "        batch_imgs = []\n",
    "        batch_labels = []\n",
    "        for line in filelists[1:]:\n",
    "            line = line.strip().split(',')\n",
    "            name = line[1]\n",
    "            label = int(line[2])\n",
    "            # 根据图片文件名加载图片，并对图像数据作预处理\n",
    "            filepath = os.path.join(datadir, name)\n",
    "            img = cv2.imread(filepath)\n",
    "            img = transform_img(img)\n",
    "            # 每读取一个样本的数据，就将其放入数据列表中\n",
    "            batch_imgs.append(img)\n",
    "            batch_labels.append(label)\n",
    "            if len(batch_imgs) == batch_size:\n",
    "                # 当数据列表的长度等于batch_size的时候，\n",
    "                # 把这些数据当作一个mini-batch，并作为数据生成器的一个输出\n",
    "                imgs_array = np.array(batch_imgs).astype('float32')\n",
    "                labels_array = np.array(batch_labels).astype('float32').reshape(-1, 1)\n",
    "                yield imgs_array, labels_array\n",
    "                batch_imgs = []\n",
    "                batch_labels = []\n",
    "\n",
    "        if len(batch_imgs) > 0:\n",
    "            # 剩余样本数目不足一个batch_size的数据，一起打包成一个mini-batch\n",
    "            imgs_array = np.array(batch_imgs).astype('float32')\n",
    "            labels_array = np.array(batch_labels).astype('float32').reshape(-1, 1)\n",
    "            yield imgs_array, labels_array\n",
    "\n",
    "    return reader\n",
    "\n",
    "# -*- coding: utf-8 -*-\n",
    "\n",
    "# LeNet 识别眼疾图片\n",
    "\n",
    "import os\n",
    "import random\n",
    "import paddle\n",
    "import paddle.fluid as fluid\n",
    "import numpy as np\n",
    "\n",
    "DATADIR = '/home/aistudio/work/palm/PALM-Training400/PALM-Training400'\n",
    "DATADIR2 = '/home/aistudio/work/palm/PALM-Validation400'\n",
    "CSVFILE = '/home/aistudio/labels.csv'\n",
    "iter=0\n",
    "iters=[]\n",
    "losses1 = []\n",
    "# 定义训练过程\n",
    "def train(model):\n",
    "    with fluid.dygraph.guard():\n",
    "        print('start training ... ')\n",
    "        model.train()\n",
    "        epoch_num = 10\n",
    "        # 定义优化器\n",
    "        \n",
    "        opt = fluid.optimizer.FtrlOptimizer(\n",
    "            learning_rate=0.001,\n",
    "            l1=-0.001, \n",
    "            l2=0.001,\n",
    "            lr_power=-0.5,\n",
    "            parameter_list=model.parameters(),\n",
    "            regularization = fluid.regularizer.L2Decay(regularization_coeff=0.001),\n",
    "            grad_clip=fluid.clip.GradientClipByValue(min=-0.001, max=0.001)\n",
    "            )\n",
    "        # 定义数据读取器，训练数据读取器和验证数据读取器\n",
    "        train_loader = data_loader(DATADIR, batch_size=10, mode='train')\n",
    "        valid_loader = valid_data_loader(DATADIR2, CSVFILE)\n",
    "\n",
    "        for epoch in range(epoch_num):\n",
    "            for batch_id, data in enumerate(train_loader()):\n",
    "                x_data, y_data = data\n",
    "                img = fluid.dygraph.to_variable(x_data)\n",
    "                label = fluid.dygraph.to_variable(y_data)\n",
    "                # 运行模型前向计算，得到预测值\n",
    "                logits = model(img)\n",
    "                # 进行loss计算\n",
    "                loss = fluid.layers.sigmoid_cross_entropy_with_logits(logits, label)\n",
    "                avg_loss = fluid.layers.mean(loss)\n",
    "\n",
    "                if batch_id % 10 == 0:\n",
    "                    print(\"epoch: {}, batch_id: {}, loss is: {}\".format(epoch, batch_id, avg_loss.numpy()))\n",
    "                    global iter\n",
    "                    global iters\n",
    "                    global losses1\n",
    "                    iters.append(iter)\n",
    "                    losses1.append(avg_loss.numpy())\n",
    "                    iter = iter + 10\n",
    "                # 反向传播，更新权重，清除梯度\n",
    "                avg_loss.backward()\n",
    "                opt.minimize(avg_loss)\n",
    "                model.clear_gradients()\n",
    "\n",
    "            model.eval()\n",
    "            accuracies = []\n",
    "\n",
    "            losses = []\n",
    "            for batch_id, data in enumerate(valid_loader()):\n",
    "                x_data, y_data = data\n",
    "                img = fluid.dygraph.to_variable(x_data)\n",
    "                label = fluid.dygraph.to_variable(y_data)\n",
    "                # 运行模型前向计算，得到预测值\n",
    "                logits = model(img)\n",
    "                # 二分类，sigmoid计算后的结果以0.5为阈值分两个类别\n",
    "                # 计算sigmoid后的预测概率，进行loss计算\n",
    "                pred = fluid.layers.sigmoid(logits)\n",
    "                loss = fluid.layers.sigmoid_cross_entropy_with_logits(logits, label)\n",
    "                # 计算预测概率小于0.5的类别\n",
    "                pred2 = pred * (-1.0) + 1.0\n",
    "                # 得到两个类别的预测概率，并沿第一个维度级联\n",
    "                pred = fluid.layers.concat([pred2, pred], axis=1)\n",
    "                acc = fluid.layers.accuracy(pred, fluid.layers.cast(label, dtype='int64'))\n",
    "                accuracies.append(acc.numpy())\n",
    "                \n",
    "\n",
    "                \n",
    "                losses.append(loss.numpy())\n",
    "                \n",
    "                \n",
    "\n",
    "\n",
    "            print(\"[validation] accuracy/loss: {}/{}\".format(np.mean(accuracies), np.mean(losses)))\n",
    "            model.train()\n",
    "\n",
    "        # save params of model\n",
    "        fluid.save_dygraph(model.state_dict(), 'palm')\n",
    "        # save optimizer state\n",
    "        fluid.save_dygraph(opt.state_dict(), 'palm')\n",
    "\n",
    "\n",
    "# 定义评估过程\n",
    "def evaluation(model, params_file_path):\n",
    "    with fluid.dygraph.guard():\n",
    "        print('start evaluation .......')\n",
    "        #加载模型参数\n",
    "        model_state_dict, _ = fluid.load_dygraph(params_file_path)\n",
    "        model.load_dict(model_state_dict)\n",
    "\n",
    "        model.eval()\n",
    "        eval_loader = data_loader(DATADIR, \n",
    "                           batch_size=10, mode='eval')\n",
    "\n",
    "        acc_set = []\n",
    "        avg_loss_set = []\n",
    "        for batch_id, data in enumerate(eval_loader()):\n",
    "            x_data, y_data = data\n",
    "            img = fluid.dygraph.to_variable(x_data)\n",
    "            label = fluid.dygraph.to_variable(y_data)\n",
    "            y_data = y_data.astype(np.int64)\n",
    "            label_64 = fluid.dygraph.to_variable(y_data)\n",
    "            # 计算预测和精度\n",
    "            prediction, acc = model(img, label_64)\n",
    "            # 计算损失函数值\n",
    "            loss = fluid.layers.sigmoid_cross_entropy_with_logits(prediction, label)\n",
    "            avg_loss = fluid.layers.mean(loss)\n",
    "            acc_set.append(float(acc.numpy()))\n",
    "            avg_loss_set.append(float(avg_loss.numpy()))\n",
    "        # 求平均精度\n",
    "        acc_val_mean = np.array(acc_set).mean()\n",
    "        avg_loss_val_mean = np.array(avg_loss_set).mean()\n",
    "\n",
    "        print('loss={}, acc={}'.format(avg_loss_val_mean, acc_val_mean))\n",
    "\n",
    "\n",
    "\n",
    "# -*- coding:utf-8 -*-\n",
    "\n",
    "# ResNet模型代码\n",
    "import numpy as np\n",
    "import paddle\n",
    "import paddle.fluid as fluid\n",
    "from paddle.fluid.layer_helper import LayerHelper\n",
    "from paddle.fluid.dygraph.nn import Conv2D, Pool2D, BatchNorm, Linear\n",
    "from paddle.fluid.dygraph.base import to_variable\n",
    "\n",
    "# ResNet中使用了BatchNorm层，在卷积层的后面加上BatchNorm以提升数值稳定性\n",
    "# 定义卷积批归一化块\n",
    "class ConvBNLayer(fluid.dygraph.Layer):\n",
    "    def __init__(self,\n",
    "                 num_channels,\n",
    "                 num_filters,\n",
    "                 filter_size,\n",
    "                 stride=1,\n",
    "                 groups=1,\n",
    "                 act=None):\n",
    "        \"\"\"\n",
    "        \n",
    "        num_channels, 卷积层的输入通道数\n",
    "        num_filters, 卷积层的输出通道数\n",
    "        stride, 卷积层的步幅\n",
    "        groups, 分组卷积的组数，默认groups=1不使用分组卷积\n",
    "        act, 激活函数类型，默认act=None不使用激活函数\n",
    "        \"\"\"\n",
    "        super(ConvBNLayer, self).__init__()\n",
    "\n",
    "        # 创建卷积层\n",
    "        self._conv = Conv2D(\n",
    "            num_channels=num_channels,\n",
    "            num_filters=num_filters,\n",
    "            filter_size=filter_size,\n",
    "            stride=stride,\n",
    "            padding=(filter_size - 1) // 2,\n",
    "            groups=groups,\n",
    "            act=None,\n",
    "            bias_attr=False)\n",
    "\n",
    "        # 创建BatchNorm层\n",
    "        self._batch_norm = BatchNorm(num_filters, act=act)\n",
    "\n",
    "    def forward(self, inputs):\n",
    "        y = self._conv(inputs)\n",
    "        y = self._batch_norm(y)\n",
    "        return y\n",
    "\n",
    "# 定义残差块\n",
    "# 每个残差块会对输入图片做三次卷积，然后跟输入图片进行短接\n",
    "# 如果残差块中第三次卷积输出特征图的形状与输入不一致，则对输入图片做1x1卷积，将其输出形状调整成一致\n",
    "class BottleneckBlock(fluid.dygraph.Layer):\n",
    "    def __init__(self,\n",
    "                 num_channels,\n",
    "                 num_filters,\n",
    "                 stride,\n",
    "                 shortcut=True):\n",
    "        super(BottleneckBlock, self).__init__()\n",
    "        # 创建第一个卷积层 1x1\n",
    "        self.conv0 = ConvBNLayer(\n",
    "            num_channels=num_channels,\n",
    "            num_filters=num_filters,\n",
    "            filter_size=1,\n",
    "            act='relu')\n",
    "        # 创建第二个卷积层 3x3\n",
    "        self.conv1 = ConvBNLayer(\n",
    "            num_channels=num_filters,\n",
    "            num_filters=num_filters,\n",
    "            filter_size=3,\n",
    "            stride=stride,\n",
    "            act='relu')\n",
    "        # 创建第三个卷积 1x1，但输出通道数乘以4\n",
    "        self.conv2 = ConvBNLayer(\n",
    "            num_channels=num_filters,\n",
    "            num_filters=num_filters * 4,\n",
    "            filter_size=1,\n",
    "            act=None)\n",
    "\n",
    "        # 如果conv2的输出跟此残差块的输入数据形状一致，则shortcut=True\n",
    "        # 否则shortcut = False，添加1个1x1的卷积作用在输入数据上，使其形状变成跟conv2一致\n",
    "        if not shortcut:\n",
    "            self.short = ConvBNLayer(\n",
    "                num_channels=num_channels,\n",
    "                num_filters=num_filters * 4,\n",
    "                filter_size=1,\n",
    "                stride=stride)\n",
    "\n",
    "        self.shortcut = shortcut\n",
    "\n",
    "        self._num_channels_out = num_filters * 4\n",
    "\n",
    "    def forward(self, inputs):\n",
    "        y = self.conv0(inputs)\n",
    "        conv1 = self.conv1(y)\n",
    "        conv2 = self.conv2(conv1)\n",
    "\n",
    "        # 如果shortcut=True，直接将inputs跟conv2的输出相加\n",
    "        # 否则需要对inputs进行一次卷积，将形状调整成跟conv2输出一致\n",
    "        if self.shortcut:\n",
    "            short = inputs\n",
    "        else:\n",
    "            short = self.short(inputs)\n",
    "\n",
    "        y = fluid.layers.elementwise_add(x=short, y=conv2)\n",
    "        layer_helper = LayerHelper(self.full_name(), act='relu')\n",
    "        return layer_helper.append_activation(y)\n",
    "\n",
    "# 定义ResNet模型\n",
    "class ResNet(fluid.dygraph.Layer):\n",
    "    def __init__(self, layers=50, class_dim=1):\n",
    "        \"\"\"\n",
    "        \n",
    "        layers, 网络层数，可以是50, 101或者152\n",
    "        class_dim，分类标签的类别数\n",
    "        \"\"\"\n",
    "        super(ResNet, self).__init__()\n",
    "        self.layers = layers\n",
    "        supported_layers = [50, 101, 152]\n",
    "        assert layers in supported_layers, \\\n",
    "            \"supported layers are {} but input layer is {}\".format(supported_layers, layers)\n",
    "\n",
    "        if layers == 50:\n",
    "            #ResNet50包含多个模块，其中第2到第5个模块分别包含3、4、6、3个残差块\n",
    "            depth = [3, 4, 6, 3]\n",
    "        elif layers == 101:\n",
    "            #ResNet101包含多个模块，其中第2到第5个模块分别包含3、4、23、3个残差块\n",
    "            depth = [3, 4, 23, 3]\n",
    "        elif layers == 152:\n",
    "            #ResNet50包含多个模块，其中第2到第5个模块分别包含3、8、36、3个残差块\n",
    "            depth = [3, 8, 36, 3]\n",
    "        \n",
    "        # 残差块中使用到的卷积的输出通道数\n",
    "        num_filters = [64, 128, 256, 512]\n",
    "\n",
    "        # ResNet的第一个模块，包含1个7x7卷积，后面跟着1个最大池化层\n",
    "        self.conv = ConvBNLayer(\n",
    "            num_channels=3,\n",
    "            num_filters=64,\n",
    "            filter_size=7,\n",
    "            stride=2,\n",
    "            act='relu')\n",
    "        self.pool2d_max = Pool2D(\n",
    "            pool_size=3,\n",
    "            pool_stride=2,\n",
    "            pool_padding=1,\n",
    "            pool_type='max')\n",
    "\n",
    "        # ResNet的第二到第五个模块c2、c3、c4、c5\n",
    "        self.bottleneck_block_list = []\n",
    "        num_channels = 64\n",
    "        for block in range(len(depth)):\n",
    "            shortcut = False\n",
    "            for i in range(depth[block]):\n",
    "                bottleneck_block = self.add_sublayer(\n",
    "                    'bb_%d_%d' % (block, i),\n",
    "                    BottleneckBlock(\n",
    "                        num_channels=num_channels,\n",
    "                        num_filters=num_filters[block],\n",
    "                        stride=2 if i == 0 and block != 0 else 1, # c3、c4、c5将会在第一个残差块使用stride=2；其余所有残差块stride=1\n",
    "                        shortcut=shortcut))\n",
    "                num_channels = bottleneck_block._num_channels_out\n",
    "                self.bottleneck_block_list.append(bottleneck_block)\n",
    "                shortcut = True\n",
    "\n",
    "        # 在c5的输出特征图上使用全局池化\n",
    "        self.pool2d_avg = Pool2D(pool_size=7, pool_type='avg', global_pooling=True)\n",
    "\n",
    "        # stdv用来作为全连接层随机初始化参数的方差\n",
    "        import math\n",
    "        stdv = 1.0 / math.sqrt(2048 * 1.0)\n",
    "        \n",
    "        # 创建全连接层，输出大小为类别数目\n",
    "        self.out = Linear(input_dim=2048, output_dim=class_dim,\n",
    "                      param_attr=fluid.param_attr.ParamAttr(\n",
    "                          initializer=fluid.initializer.Uniform(-stdv, stdv)))\n",
    "\n",
    "        \n",
    "    def forward(self, inputs):\n",
    "        y = self.conv(inputs)\n",
    "        y = self.pool2d_max(y)\n",
    "        for bottleneck_block in self.bottleneck_block_list:\n",
    "            y = bottleneck_block(y)\n",
    "        y = self.pool2d_avg(y)\n",
    "        y = fluid.layers.reshape(y, [y.shape[0], -1])\n",
    "        y = self.out(y)\n",
    "        return y\n",
    "\n",
    "with fluid.dygraph.guard():\n",
    "    model = ResNet()\n",
    "\n",
    "train(model)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#画出训练过程中Loss的变化曲线\n",
    "\n",
    "plt.figure()\n",
    "plt.title(\"train loss\", fontsize=24)\n",
    "plt.xlabel(\"iter\", fontsize=14)\n",
    "plt.ylabel(\"loss\", fontsize=14)\n",
    "plt.plot(iters, losses1,color='red',label='train loss') \n",
    "plt.grid()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# 下面的代码是测试集代码\n",
    "## 第三部分\n",
    "> 是完成的作业，使用的是测试集的数据+loss图表构成"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "start training ... \n",
      "验证集数据读取器,验证集地址： <function valid_data_loader at 0x7f47dc562ef0>\n",
      "epoch: 0, batch_id: 0, loss is: [0.66134024]\n",
      "epoch: 0, batch_id: 10, loss is: [0.5367668]\n",
      "epoch: 0, batch_id: 20, loss is: [0.2453782]\n",
      "epoch: 0, batch_id: 30, loss is: [0.16095015]\n",
      "[validation] accuracy/loss: 0.925000011920929/0.21005886793136597\n",
      "epoch: 1, batch_id: 0, loss is: [0.10243329]\n",
      "epoch: 1, batch_id: 10, loss is: [0.55691475]\n",
      "epoch: 1, batch_id: 20, loss is: [0.5431757]\n",
      "epoch: 1, batch_id: 30, loss is: [0.15497321]\n",
      "[validation] accuracy/loss: 0.95250004529953/0.15944933891296387\n",
      "epoch: 2, batch_id: 0, loss is: [0.7892511]\n",
      "epoch: 2, batch_id: 10, loss is: [0.46356893]\n",
      "epoch: 2, batch_id: 20, loss is: [0.52403665]\n",
      "epoch: 2, batch_id: 30, loss is: [0.45690072]\n",
      "[validation] accuracy/loss: 0.9575001001358032/0.14972203969955444\n",
      "epoch: 3, batch_id: 0, loss is: [0.09580807]\n",
      "epoch: 3, batch_id: 10, loss is: [0.17639945]\n",
      "epoch: 3, batch_id: 20, loss is: [0.3002528]\n",
      "epoch: 3, batch_id: 30, loss is: [0.20991382]\n",
      "[validation] accuracy/loss: 0.9600000381469727/0.14610393345355988\n",
      "epoch: 4, batch_id: 0, loss is: [0.4419187]\n",
      "epoch: 4, batch_id: 10, loss is: [0.22392908]\n",
      "epoch: 4, batch_id: 20, loss is: [0.08607683]\n",
      "epoch: 4, batch_id: 30, loss is: [1.0288093]\n",
      "[validation] accuracy/loss: 0.9649999737739563/0.14169517159461975\n",
      "epoch: 5, batch_id: 0, loss is: [0.1463066]\n",
      "epoch: 5, batch_id: 10, loss is: [0.07737597]\n",
      "epoch: 5, batch_id: 20, loss is: [0.26133832]\n",
      "epoch: 5, batch_id: 30, loss is: [0.20391901]\n",
      "[validation] accuracy/loss: 0.9649999737739563/0.13189563155174255\n",
      "epoch: 6, batch_id: 0, loss is: [0.09324831]\n",
      "epoch: 6, batch_id: 10, loss is: [0.48724627]\n",
      "epoch: 6, batch_id: 20, loss is: [0.05345251]\n",
      "epoch: 6, batch_id: 30, loss is: [0.24536334]\n",
      "[validation] accuracy/loss: 0.9675000309944153/0.1302986592054367\n",
      "epoch: 7, batch_id: 0, loss is: [0.12577689]\n",
      "epoch: 7, batch_id: 10, loss is: [0.07790846]\n",
      "epoch: 7, batch_id: 20, loss is: [0.06873655]\n",
      "epoch: 7, batch_id: 30, loss is: [0.36013338]\n",
      "[validation] accuracy/loss: 0.9725000262260437/0.11571560055017471\n",
      "epoch: 8, batch_id: 0, loss is: [0.3702302]\n",
      "epoch: 8, batch_id: 10, loss is: [0.08176058]\n",
      "epoch: 8, batch_id: 20, loss is: [0.09361052]\n",
      "epoch: 8, batch_id: 30, loss is: [0.41822654]\n",
      "[validation] accuracy/loss: 0.9725000262260437/0.116694375872612\n",
      "epoch: 9, batch_id: 0, loss is: [0.07693991]\n",
      "epoch: 9, batch_id: 10, loss is: [0.0659496]\n",
      "epoch: 9, batch_id: 20, loss is: [0.2596845]\n",
      "epoch: 9, batch_id: 30, loss is: [0.3155311]\n",
      "[validation] accuracy/loss: 0.9675000309944153/0.11628947407007217\n"
     ]
    }
   ],
   "source": [
    "import cv2\n",
    "import random\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# 对读入的图像数据进行预处理\n",
    "def transform_img(img):\n",
    "    # 将图片尺寸缩放道 224x224\n",
    "    img = cv2.resize(img, (224, 224))\n",
    "    # 读入的图像数据格式是[H, W, C]\n",
    "    # 使用转置操作将其变成[C, H, W]\n",
    "    img = np.transpose(img, (2,0,1))\n",
    "    img = img.astype('float32')\n",
    "    # 将数据范围调整到[-1.0, 1.0]之间\n",
    "    img = img / 255.\n",
    "    img = img * 2.0 - 1.0\n",
    "    return img\n",
    "\n",
    "# 定义验证集数据读取器\n",
    "def valid_data_loader(datadir, csvfile, batch_size=10, mode='valid'):\n",
    "    print('验证集数据读取器,验证集地址：', valid_data_loader)\n",
    "    # 训练集读取时通过文件名来确定样本标签，验证集则通过csvfile来读取每个图片对应的标签\n",
    "    # 请查看解压后的验证集标签数据，观察csvfile文件里面所包含的内容\n",
    "    # csvfile文件所包含的内容格式如下，每一行代表一个样本，\n",
    "    # 其中第一列是图片id，第二列是文件名，第三列是图片标签，\n",
    "    # 第四列和第五列是Fovea的坐标，与分类任务无关\n",
    "    # ID,imgName,Label,Fovea_X,Fovea_Y\n",
    "    # 1,V0001.jpg,0,1157.74,1019.87\n",
    "    # 2,V0002.jpg,1,1285.82,1080.47\n",
    "    # 打开包含验证集标签的csvfile，并读入其中的内容\n",
    "    filelists = open(csvfile).readlines()\n",
    "    def reader():\n",
    "        batch_imgs = []\n",
    "        batch_labels = []\n",
    "        for line in filelists[1:]:\n",
    "            line = line.strip().split(',')\n",
    "            name = line[1]\n",
    "            label = int(line[2])\n",
    "            # 根据图片文件名加载图片，并对图像数据作预处理\n",
    "            filepath = os.path.join(datadir, name)\n",
    "            img = cv2.imread(filepath)\n",
    "            img = transform_img(img)\n",
    "            # 每读取一个样本的数据，就将其放入数据列表中\n",
    "            batch_imgs.append(img)\n",
    "            batch_labels.append(label)\n",
    "            if len(batch_imgs) == batch_size:\n",
    "                # 当数据列表的长度等于batch_size的时候，\n",
    "                # 把这些数据当作一个mini-batch，并作为数据生成器的一个输出\n",
    "                imgs_array = np.array(batch_imgs).astype('float32')\n",
    "                labels_array = np.array(batch_labels).astype('float32').reshape(-1, 1)\n",
    "                yield imgs_array, labels_array\n",
    "                batch_imgs = []\n",
    "                batch_labels = []\n",
    "\n",
    "        if len(batch_imgs) > 0:\n",
    "            # 剩余样本数目不足一个batch_size的数据，一起打包成一个mini-batch\n",
    "            imgs_array = np.array(batch_imgs).astype('float32')\n",
    "            labels_array = np.array(batch_labels).astype('float32').reshape(-1, 1)\n",
    "            yield imgs_array, labels_array\n",
    "\n",
    "    return reader\n",
    "\n",
    "\n",
    "# LeNet 识别眼疾图片\n",
    "\n",
    "import os\n",
    "import random\n",
    "import paddle\n",
    "import paddle.fluid as fluid\n",
    "import numpy as np\n",
    "\n",
    "DATADIR = '/home/aistudio/work/palm/PALM-Training400/PALM-Training400'\n",
    "DATADIR2 = '/home/aistudio/work/palm/PALM-Validation400'\n",
    "CSVFILE = '/home/aistudio/labels.csv'\n",
    "iter=0\n",
    "iters=[]\n",
    "losses1 = []\n",
    "\n",
    "\n",
    "# 定义评估过程\n",
    "def evaluation(model, params_file_path):\n",
    "    with fluid.dygraph.guard():\n",
    "        print('开始评估 .......')\n",
    "        #加载模型参数\n",
    "        model_state_dict, _ = fluid.load_dygraph(params_file_path)\n",
    "        model.load_dict(model_state_dict)\n",
    "\n",
    "        model.eval()\n",
    "        eval_loader = data_loader(DATADIR2, \n",
    "                           batch_size=10, mode='eval')\n",
    "\n",
    "        acc_set = []\n",
    "        avg_loss_set = []\n",
    "        for batch_id, data in enumerate(eval_loader()):\n",
    "            x_data, y_data = data\n",
    "            img = fluid.dygraph.to_variable(x_data)\n",
    "            label = fluid.dygraph.to_variable(y_data)\n",
    "            y_data = y_data.astype(np.int64)\n",
    "            label_64 = fluid.dygraph.to_variable(y_data)\n",
    "            # 计算预测和精度\n",
    "            prediction, acc = model(img, label_64)\n",
    "            # 计算损失函数值\n",
    "            loss = fluid.layers.sigmoid_cross_entropy_with_logits(prediction, label)\n",
    "            avg_loss = fluid.layers.mean(loss)\n",
    "            acc_set.append(float(acc.numpy()))\n",
    "            avg_loss_set.append(float(avg_loss.numpy()))\n",
    "        # 求平均精度\n",
    "        acc_val_mean = np.array(acc_set).mean()\n",
    "        avg_loss_val_mean = np.array(avg_loss_set).mean()\n",
    "\n",
    "        print('loss={}, acc={}'.format(avg_loss_val_mean, acc_val_mean))\n",
    "        global iter\n",
    "        global iters\n",
    "        global losses1\n",
    "        iters.append(iter)\n",
    "        losses1.append(avg_loss_val_mean.numpy())\n",
    "        iter = iter + 10\n",
    "\n",
    "\n",
    "\n",
    "# -*- coding:utf-8 -*-\n",
    "\n",
    "# ResNet模型代码\n",
    "import numpy as np\n",
    "import paddle\n",
    "import paddle.fluid as fluid\n",
    "from paddle.fluid.layer_helper import LayerHelper\n",
    "from paddle.fluid.dygraph.nn import Conv2D, Pool2D, BatchNorm, Linear\n",
    "from paddle.fluid.dygraph.base import to_variable\n",
    "\n",
    "# ResNet中使用了BatchNorm层，在卷积层的后面加上BatchNorm以提升数值稳定性\n",
    "# 定义卷积批归一化块\n",
    "class ConvBNLayer(fluid.dygraph.Layer):\n",
    "    def __init__(self,\n",
    "                 num_channels,\n",
    "                 num_filters,\n",
    "                 filter_size,\n",
    "                 stride=1,\n",
    "                 groups=1,\n",
    "                 act=None):\n",
    "        \"\"\"\n",
    "        \n",
    "        num_channels, 卷积层的输入通道数\n",
    "        num_filters, 卷积层的输出通道数\n",
    "        stride, 卷积层的步幅\n",
    "        groups, 分组卷积的组数，默认groups=1不使用分组卷积\n",
    "        act, 激活函数类型，默认act=None不使用激活函数\n",
    "        \"\"\"\n",
    "        super(ConvBNLayer, self).__init__()\n",
    "\n",
    "        # 创建卷积层\n",
    "        self._conv = Conv2D(\n",
    "            num_channels=num_channels,\n",
    "            num_filters=num_filters,\n",
    "            filter_size=filter_size,\n",
    "            stride=stride,\n",
    "            padding=(filter_size - 1) // 2,\n",
    "            groups=groups,\n",
    "            act=None,\n",
    "            bias_attr=False)\n",
    "\n",
    "        # 创建BatchNorm层\n",
    "        self._batch_norm = BatchNorm(num_filters, act=act)\n",
    "\n",
    "    def forward(self, inputs):\n",
    "        y = self._conv(inputs)\n",
    "        y = self._batch_norm(y)\n",
    "        return y\n",
    "\n",
    "# 定义残差块\n",
    "# 每个残差块会对输入图片做三次卷积，然后跟输入图片进行短接\n",
    "# 如果残差块中第三次卷积输出特征图的形状与输入不一致，则对输入图片做1x1卷积，将其输出形状调整成一致\n",
    "class BottleneckBlock(fluid.dygraph.Layer):\n",
    "    def __init__(self,\n",
    "                 num_channels,\n",
    "                 num_filters,\n",
    "                 stride,\n",
    "                 shortcut=True):\n",
    "        super(BottleneckBlock, self).__init__()\n",
    "        # 创建第一个卷积层 1x1\n",
    "        self.conv0 = ConvBNLayer(\n",
    "            num_channels=num_channels,\n",
    "            num_filters=num_filters,\n",
    "            filter_size=1,\n",
    "            act='relu')\n",
    "        # 创建第二个卷积层 3x3\n",
    "        self.conv1 = ConvBNLayer(\n",
    "            num_channels=num_filters,\n",
    "            num_filters=num_filters,\n",
    "            filter_size=3,\n",
    "            stride=stride,\n",
    "            act='relu')\n",
    "        # 创建第三个卷积 1x1，但输出通道数乘以4\n",
    "        self.conv2 = ConvBNLayer(\n",
    "            num_channels=num_filters,\n",
    "            num_filters=num_filters * 4,\n",
    "            filter_size=1,\n",
    "            act=None)\n",
    "\n",
    "        # 如果conv2的输出跟此残差块的输入数据形状一致，则shortcut=True\n",
    "        # 否则shortcut = False，添加1个1x1的卷积作用在输入数据上，使其形状变成跟conv2一致\n",
    "        if not shortcut:\n",
    "            self.short = ConvBNLayer(\n",
    "                num_channels=num_channels,\n",
    "                num_filters=num_filters * 4,\n",
    "                filter_size=1,\n",
    "                stride=stride)\n",
    "\n",
    "        self.shortcut = shortcut\n",
    "\n",
    "        self._num_channels_out = num_filters * 4\n",
    "\n",
    "    def forward(self, inputs):\n",
    "        y = self.conv0(inputs)\n",
    "        conv1 = self.conv1(y)\n",
    "        conv2 = self.conv2(conv1)\n",
    "\n",
    "        # 如果shortcut=True，直接将inputs跟conv2的输出相加\n",
    "        # 否则需要对inputs进行一次卷积，将形状调整成跟conv2输出一致\n",
    "        if self.shortcut:\n",
    "            short = inputs\n",
    "        else:\n",
    "            short = self.short(inputs)\n",
    "\n",
    "        y = fluid.layers.elementwise_add(x=short, y=conv2)\n",
    "        layer_helper = LayerHelper(self.full_name(), act='relu')\n",
    "        return layer_helper.append_activation(y)\n",
    "\n",
    "# 定义ResNet模型\n",
    "class ResNet(fluid.dygraph.Layer):\n",
    "    def __init__(self, layers=50, class_dim=1):\n",
    "        \"\"\"\n",
    "        \n",
    "        layers, 网络层数，可以是50, 101或者152\n",
    "        class_dim，分类标签的类别数\n",
    "        \"\"\"\n",
    "        super(ResNet, self).__init__()\n",
    "        self.layers = layers\n",
    "        supported_layers = [50, 101, 152]\n",
    "        assert layers in supported_layers, \\\n",
    "            \"支持的图层有 {} ----输入层是 {}\".format(supported_layers, layers)\n",
    "\n",
    "        if layers == 50:\n",
    "            #ResNet50包含多个模块，其中第2到第5个模块分别包含3、4、6、3个残差块\n",
    "            depth = [3, 4, 6, 3]\n",
    "        elif layers == 101:\n",
    "            #ResNet101包含多个模块，其中第2到第5个模块分别包含3、4、23、3个残差块\n",
    "            depth = [3, 4, 23, 3]\n",
    "        elif layers == 152:\n",
    "            #ResNet50包含多个模块，其中第2到第5个模块分别包含3、8、36、3个残差块\n",
    "            depth = [3, 8, 36, 3]\n",
    "        \n",
    "        # 残差块中使用到的卷积的输出通道数\n",
    "        num_filters = [64, 128, 256, 512]\n",
    "\n",
    "        # ResNet的第一个模块，包含1个7x7卷积，后面跟着1个最大池化层\n",
    "        self.conv = ConvBNLayer(\n",
    "            num_channels=3,\n",
    "            num_filters=64,\n",
    "            filter_size=7,\n",
    "            stride=2,\n",
    "            act='relu')\n",
    "        self.pool2d_max = Pool2D(\n",
    "            pool_size=3,\n",
    "            pool_stride=2,\n",
    "            pool_padding=1,\n",
    "            pool_type='max')\n",
    "\n",
    "        # ResNet的第二到第五个模块c2、c3、c4、c5\n",
    "        self.bottleneck_block_list = []\n",
    "        num_channels = 64\n",
    "        for block in range(len(depth)):\n",
    "            shortcut = False\n",
    "            for i in range(depth[block]):\n",
    "                bottleneck_block = self.add_sublayer(\n",
    "                    'bb_%d_%d' % (block, i),\n",
    "                    BottleneckBlock(\n",
    "                        num_channels=num_channels,\n",
    "                        num_filters=num_filters[block],\n",
    "                        stride=2 if i == 0 and block != 0 else 1, # c3、c4、c5将会在第一个残差块使用stride=2；其余所有残差块stride=1\n",
    "                        shortcut=shortcut))\n",
    "                num_channels = bottleneck_block._num_channels_out\n",
    "                self.bottleneck_block_list.append(bottleneck_block)\n",
    "                shortcut = True\n",
    "\n",
    "        # 在c5的输出特征图上使用全局池化\n",
    "        self.pool2d_avg = Pool2D(pool_size=7, pool_type='avg', global_pooling=True)\n",
    "\n",
    "        # stdv用来作为全连接层随机初始化参数的方差\n",
    "        import math\n",
    "        stdv = 1.0 / math.sqrt(2048 * 1.0)\n",
    "        \n",
    "        # 创建全连接层，输出大小为类别数目\n",
    "        self.out = Linear(input_dim=2048, output_dim=class_dim,\n",
    "                      param_attr=fluid.param_attr.ParamAttr(\n",
    "                          initializer=fluid.initializer.Uniform(-stdv, stdv)))\n",
    "\n",
    "        \n",
    "    def forward(self, inputs):\n",
    "        y = self.conv(inputs)\n",
    "        y = self.pool2d_max(y)\n",
    "        for bottleneck_block in self.bottleneck_block_list:\n",
    "            y = bottleneck_block(y)\n",
    "        y = self.pool2d_avg(y)\n",
    "        y = fluid.layers.reshape(y, [y.shape[0], -1])\n",
    "        y = self.out(y)\n",
    "        return y\n",
    "\n",
    "with fluid.dygraph.guard():\n",
    "    model = ResNet()\n",
    "\n",
    "train(model)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#画出训练过程中Loss的变化曲线\n",
    "\n",
    "plt.figure()\n",
    "plt.title(\"valid loss\", fontsize=24)\n",
    "plt.xlabel(\"iter\", fontsize=14)\n",
    "plt.ylabel(\"loss\", fontsize=14)\n",
    "plt.plot(iters, losses1,color='red',label='valid loss') \n",
    "plt.grid()\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "PaddlePaddle 1.8.0 (Python 3.5)",
   "language": "python",
   "name": "py35-paddle1.2.0"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
